package sync

import (
	"io"
	"runtime"
	"sync/atomic"

	"gitee.com/yrwy/msgo/pkg/errors"
)

// stopper与closer的区别在于，Close()返回error，而Stop()一直阻塞到完成并停止
type Closer interface {
	io.Closer
	IsClosed() bool
	Done() <-chan struct{}
	Wait()
}

// closedchan is a reusable closed channel.
var closedchan = make(chan struct{})

func init() {
	close(closedchan)
}

type waitClose struct {
	stop atomic.Value //存储chan struct{}
}

// 构造一个Closer
func NewCloser() Closer {
	s := make(chan struct{})
	w := &waitClose{}
	w.stop.Store(s)
	runtime.SetFinalizer(w, func(wc *waitClose) {
		wc.Close() //if not close chan, this is can close the chan
	})
	return w
}

func NewClosed() Closer {
	return &waitClose{}
}

// 是否已经停止
func (wc *waitClose) IsClosed() bool {
	stop := wc.stop.Load()
	return stop == nil || stop.(chan struct{}) == closedchan
}

// 接收停止信号，用于select
func (wc *waitClose) Done() <-chan struct{} {
	stop := wc.stop.Load()
	if stop != nil {
		return stop.(chan struct{})
	}
	return closedchan //返回默认的停止
}

// 停止
func (wc *waitClose) Close() error {
	stop := wc.stop.Swap(closedchan)
	if stop != nil && stop != closedchan {
		close(stop.(chan struct{}))
		return nil
	}
	return errors.ErrClosed
}

// 等待停止
func (wc *waitClose) Wait() {
	stop := wc.stop.Load()
	if stop != nil {
		<-stop.(chan struct{})
	}
}
